using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Publicuse.Entity;
using Publicuse.Util.Jwt;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Publicuse.WebCore.AuthorizationExtend
{
    public static class AuthorizationExtensions
    {
        public static void RegisterAuthorization(this WebApplicationBuilder builder)
        {
            //注册JWT
            builder.Services.Configure<JWTTokenOptions>(builder.Configuration.GetSection("JWTTokenOptions"));
            builder.Services.AddTransient<ICustomJWTService, CustomHSJWTService>();

            JWTTokenOptions tokenOptions = new JWTTokenOptions();
            builder.Configuration.Bind("JWTTokenOptions", tokenOptions);

            builder.Services.AddAuthentication(options =>
             {
               options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
               options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
             })
          .AddJwtBearer(options =>  //这里是配置的鉴权的逻辑
          {
              options.TokenValidationParameters = InitTokenValidationParameters(tokenOptions);
              //options.Events = new JwtBearerEvents
              //{
              //    //此处为权限验证失败后触发的事件，涵盖的场景：没有token的，token错误的场景，封装到InitOnChallenge方法中去
              //    OnChallenge = InitOnChallenge,

              //    //OnChallenge = context =>
              //    //{
              //    //    return Task.CompletedTask;
              //    //},

              //    //此处为权限验证失败后触发的事件，涵盖的场景：有token且token是正确的token,但是token涵盖的权限不够，封装到InitOnForbidden 方法中去
              //    OnForbidden = InitOnForbidden,
              //    //此处为权限验证成功后会执行的行为，封装到InitOnTokenValidated 方法中去
              //    OnTokenValidated = InitOnTokenValidated,
              //    //此处为权限验证异常了，封装到InitOnAuthenticationFailed 方法中去
              //    OnAuthenticationFailed = InitOnAuthenticationFailed,
              //    //此处为开始验证权限前夕可以增加的业务逻辑，封装到InitOnMessageReceived 方法中去
              //    OnMessageReceived = InitOnMessageReceived
              //};
          });

            //增加权限判定策略
            //需要结合业务来完成验证
            //1. 添加策略
            //2. 需要自定义策略
            //builder.Services.AddAuthorization(options =>
            //{
            //    options.AddPolicy("MenuPolicy", policyBuilder => policyBuilder
            //    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
            //     .AddRequirements(new MenuAuthorizeRequirement()));
            //});
            //builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizeHandler>();


            ////配置鉴权
            //builder.Services.AddAuthentication(option =>
            //{
            //    option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    option.DefaultForbidScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            //    option.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;

            //}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, option =>
            //{
            //    option.LoginPath = "/api/Menu/Fail";//如果没有找到用户信息---鉴权失败--授权也失败了---就跳转到指定的Action 
            //    option.AccessDeniedPath = "/api/Menu/Fail";
            //});

            //builder.Services.AddAuthorization(option =>
            //{
            //    option.AddPolicy("MenuPolicy", policyBuilder =>
            //    {
            //        policyBuilder.RequireAssertion(context =>
            //        {
            //            //可以在这里做业务逻辑计算
            //            bool bResult = context.User.HasClaim(c => c.Type == ClaimTypes.Role)
            //                 && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "Admin"
            //                 && context.User.Claims.Any(c => c.Type == ClaimTypes.Name);

            //            return bResult;
            //        });
            //        //获取到用户的信息之后，希望调用一些第三方的类来判定下呢？？  
            //        policyBuilder.Requirements.Add(new CustomAuthorizationRequirement());
            //        //实现一个接口 IAuthorizationHandler

            //    });
            //});  //支持授权
        }


        /// <summary>
        /// 配置默认的参数验证
        /// </summary>
        /// <param name="tokenOptions"></param>
        /// <returns></returns>
        private static TokenValidationParameters InitTokenValidationParameters(JWTTokenOptions tokenOptions) => new TokenValidationParameters
        {
            //JWT有一些默认的属性，就是给鉴权时就可以筛选了
            ValidateIssuer = true,//是否验证Issuer
            ValidateAudience = true,//是否验证Audience
            ValidateLifetime = true,//是否验证失效时间
            ValidateIssuerSigningKey = true,//是否验证SecurityKey
            ValidAudience = tokenOptions.Audience,//
            ValidIssuer = tokenOptions.Issuer,//Issuer，这两项和前面签发jwt的设置一致
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.SecurityKey)),//拿到SecurityKey 
            ClockSkew = TimeSpan.FromSeconds(0),//设置token过期后多久失效，默认过期后300秒内仍有效
                                                //AudienceValidator = (m, n, z) => true,  //自定义规则通通不验证，返回true
                                                //LifetimeValidator = (notBefore, expires, securityToken, validationParameters) => true //自定义规则通通不验证，返回true
        };


        /// <summary>
        ///  //此处为权限验证失败后触发的事件，涵盖的场景：没有token的，token错误的
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static Task InitOnChallenge(JwtBearerChallengeContext context)
        {
            //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
            context.HandleResponse();
            //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
            var payload = JsonConvert.SerializeObject(new ApiDataResult<int>()
            {
                Success = false,
                Message = "对不起没有授权，没有Token",
                Data = 0,
                OValue = 401
            }, new JsonSerializerSettings
            {
                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
            });
            //自定义返回的数据类型
            context.Response.ContentType = "application/json";
            //自定义返回状态码，默认为401 我这里改成 200
            context.Response.StatusCode = StatusCodes.Status200OK;
            //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
            //输出Json数据结果
            context.Response.WriteAsync(payload);
            return Task.FromResult(0);
        }

        /// <summary>
        ///此处为权限验证失败后触发的事件，涵盖的场景：有token且token是正确的token,但是token涵盖的权限不够
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static Task InitOnForbidden(ForbiddenContext context)
        {
            //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
            var payload = JsonConvert.SerializeObject(new ApiDataResult<int>()
            {
                Success = false,
                Message = "对不起，您不具备访问该功能的权限",
                Data = 1,
                OValue = 403
            }, new JsonSerializerSettings
            {
                ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
            });

            //自定义返回的数据类型
            context.Response.ContentType = "application/json";
            //自定义返回状态码，默认为403 我这里改成 200
            context.Response.StatusCode = StatusCodes.Status200OK;
            //context.Response.StatusCode = StatusCodes.Status403Forbidden;
            //输出Json数据结果
            context.Response.WriteAsync(payload);
            return Task.FromResult(0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static Task InitOnTokenValidated(TokenValidatedContext context)
        {
            Console.WriteLine("Token解析成功了");
            return Task.FromResult(0);
        }

        /// <summary>
        /// 此处为权限验证异常了 
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static Task InitOnAuthenticationFailed(AuthenticationFailedContext context)
        {
            Console.WriteLine("Token解析成功了");
            return Task.FromResult(0);
        }

        /// <summary>
        /// 此处为开始验证权限前夕可以增加的业务逻辑
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static Task InitOnMessageReceived(MessageReceivedContext context)
        {
            return Task.FromResult(0);
        }
    }
}
